package com.group.project.hrms.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
import com.aliyun.tea.TeaException;
import com.group.project.hrms.entity.*;
import com.group.project.hrms.entity.DingResultEntity.*;
import com.group.project.hrms.service.IDingAuthInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;
import java.util.*;

@Slf4j
public class DingAccessUtils {

    @Autowired
    private com.aliyun.dingtalkoauth2_1_0.Client client;

    @Autowired
    private IDingAuthInfoService iDingAuthInfoService;

    @Autowired
    private RestTemplate restTemplate;

    private static String token;


    /**
     * 该方法将获取企业内部应用的accessToken
     * @return 获取成功则返回Token，否则返回F (Fail)，若数据库无数据，则返回N (Nothing)
     */
    public String getAppAccessToken(){

        //获取数据库中钉钉认证的配置信息(AppKey / AppSecret)
        List<DingAuthInfo> dingAuthInfoList = iDingAuthInfoService.selectAllDingAuthInfo();
        if(null != dingAuthInfoList && dingAuthInfoList.size() != 0){
            GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest()
                    .setAppKey(dingAuthInfoList.get(0).getAppKey()) //当前版本只支持设置一个钉钉小程序认证信息
                    .setAppSecret(dingAuthInfoList.get(0).getAppSecret());
            try {
                GetAccessTokenResponse response = client.getAccessToken(getAccessTokenRequest);
                token = response.body.accessToken;
                return token;
            } catch (TeaException err) {
                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                    // err 中含有 code 和 message 属性，可帮助开发定位问题
                    log.error(err.message);
                }
            } catch (Exception _err) {
                TeaException err = new TeaException(_err.getMessage(), _err);
                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                    // err 中含有 code 和 message 属性，可帮助开发定位问题
                    log.error(err.message);
                }
            }
        }else{
            return "N";
        }
        return "F";
    }


    /**
     * 获取所有部门信息，包含所有子部门 钉钉规定顶层部门的ID为1（Long）
     * @return 部门信息对象集合(DingDeptListEntity对象集合)
     */
    public List<DingDeptListEntity> getAllDingDeptListEntityFromDing(){
        List<DingDeptListEntity> dpList = new ArrayList<>();
        if(null != DingAccessUtils.token){
            dpList = this.doGetDeptListByID(1L);
            dpList = this.doGetSubDept(dpList, this.doGetDeptListByID(1L));
        }else{
            this.getAppAccessToken();
        }
        return dpList;
    }


    /**
     * 传入部门ID，返回该部门下的部门列表，如果返回的集合数量为0，则表示该部门下已经没有子部门了或者errcode不等于0
     * @param id 部门ID
     * @return 部门信息列表集合 (DingDeptListEntity对象集合)
     */
    public List<DingDeptListEntity> doGetDeptListByID(Long id){
        List<DingDeptListEntity> list = new ArrayList<>();
        if(null != DingAccessUtils.token){
            //构建param参数
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("dept_id",id);
            JSONObject result = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetDeptList, DingAccessUtils.token, jsonObject));
            Object errcode = result.get("errcode");
            if(null != errcode && (int)errcode == 0){
                Object dpList = result.get("result");
                return JSON.parseArray(JSON.toJSONString(dpList), DingDeptListEntity.class);
            }
        }
        return list;
    }

    /**
     * 获取所有部门列表
     * @param totalList 所有部门列表集合
     * @param list 要解析的子部门列表集合
     * @return 所有部门列表集合
     */
    public List<DingDeptListEntity> doGetSubDept(List<DingDeptListEntity> totalList, List<DingDeptListEntity> list){
        if(list.size() != 0){
            for (DingDeptListEntity dp : list
            ) {
                List<DingDeptListEntity> casualList = doGetDeptListByID(dp.getDept_id());
                if(null != casualList && casualList.size() != 0){
                    totalList.addAll(casualList);
                    this.doGetSubDept(totalList, casualList);
                }
            }
        }
        return totalList;
    }


    /**
     * 获取所有部门的ID  钉钉规定顶层部门的ID为1（Long）
     * @return 部门ID集合
     */
    public List<Long> getAllDeptIDFromDing(){
        List<Long> idLists = new ArrayList<>();
        if(null != DingAccessUtils.token){
            idLists = this.getSubDeptIDsByParentID(1L);
            idLists = this.doGetSubDeptIDs(idLists, this.getSubDeptIDsByParentID(1L));
        }else {
            this.getAppAccessToken();
        }
        return idLists;
    }

    /**
     * 根据部门ID获取该部门下的子部门ID集合
     * @param id 父部门ID
     * @return 子部门ID集合
     */
    public List<Long> getSubDeptIDsByParentID(Long id){
        List<Long> ids = new ArrayList<>();
        if(null != DingAccessUtils.token){
            //构建param参数
            JSONObject param = new JSONObject();
            param.put("dept_id",id);
            JSONObject result = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetDeptIDList, DingAccessUtils.token, param));
            Object errcode = result.get("errcode");
            if(null != errcode && (int)errcode == 0){
                Object dpList = result.getJSONObject("result").get("dept_id_list");
                return JSON.parseArray(JSON.toJSONString(dpList), Long.class);
            }
        }
        return ids;
    }

    /**
     * 获取所有子部门ID集合
     * @param totalList 所有子部门ID集合
     * @param list 要继续解析的部门ID集合
     * @return 所有子部门ID集合
     */
    public List<Long> doGetSubDeptIDs(List<Long> totalList, List<Long> list){
        if(list.size() != 0){
            for (Long id : list
                 ) {
                List<Long> casualList = getSubDeptIDsByParentID(id);
                if(null != casualList && casualList.size() != 0){
                    totalList.addAll(casualList);
                    this.doGetSubDeptIDs(totalList, casualList);
                }
            }
        }
        return totalList;
    }

    /**
     * 获取钉钉中在职人员列表，钉钉规定：2：试用期 3：正式 5：待离职 -1：无状态   本程序规定：statusFlag = 0 表示要查询离职清单
     * @return 按照员工状态分类的Map集合
     */
    public Map<Integer, List<String>> getStaffIDListGroupByStatusFlag(){
        Map<Integer, List<String>> map = new HashMap();
        map.put(0, doGetStaffIDList(new ArrayList<>(), 0, 0));
        map.put(2, doGetStaffIDList(new ArrayList<>(), 2, 0));
        map.put(3, doGetStaffIDList(new ArrayList<>(), 3, 0));
        map.put(5, doGetStaffIDList(new ArrayList<>(), 5, 0));
        map.put(-1, doGetStaffIDList(new ArrayList<>(), -1, 0));
        return map;
    }

    /**
     * 获取离职、在职（2：试用期 3：正式 5：待离职 -1：无状态）人员userid列表
     * @param totalList 将分页数据累计后的集合
     * @param statusFlag 人员状态标志
     * @param offset 分页游标，从0开始。根据返回结果里的next_cursor是否为空来判断是否还有下一页，且再次调用时offset设置成next_cursor的值。
     * @return 分页数据累计后的集合
     */
    public List<String> doGetStaffIDList(List<String> totalList, int statusFlag, int offset){
        if(statusFlag == 0){ //离职人员userid列表
            JSONObject param = new JSONObject();
            param.put("offset", offset);
            param.put("size", 50);
            JSONObject res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetLeaveStaffList, DingAccessUtils.token, param)).getJSONObject("result");
            totalList.addAll(JSON.parseArray(res.getString("data_list"), String.class));
            if(null != res.getString("next_cursor")){
                this.doGetStaffIDList(totalList, 0, Integer.parseInt(res.getString("next_cursor")));
            }
        }else{ //非离职人员，将根据传入的status_list判断需要获取哪些列表
            JSONObject res;
            JSONObject param = new JSONObject();
            param.put("status_list", statusFlag);
            param.put("offset", offset);
            param.put("size", 50);
            res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetBeOnJobStaffList, DingAccessUtils.token, param)).getJSONObject("result");
            totalList.addAll(JSON.parseArray(res.getString("data_list"), String.class));
            if(null != res.get("next_cursor")){
                this.doGetStaffIDList(totalList, statusFlag, Integer.parseInt(res.getString("next_cursor")));
            }
        }
        return totalList;
    }

    /**
     * 根据所有部门列表查询部门详情获取对应的部门员工详情信息
     * @return 所有部门员工详情信息集合
     */
    public List<DingDeptStaffDetails> getAllDingDeptStaffDetailsFromDing(){
        List<DingDeptStaffDetails> dingDeptStaffDetails = new ArrayList<>();
        dingDeptStaffDetails.addAll(this.doRootDeptStaffDetailsFromDing());
        dingDeptStaffDetails.addAll(this.doGetAllSubDeptStaffDetailsFromDing(this.getAllDingDeptListEntityFromDing()));
        return dingDeptStaffDetails;
    }

    /**
     * 获取根部门下的员工详情
     * @return 员工详情数据集合 List<DingDeptStaffDetails>
     */
    public List<DingDeptStaffDetails> doRootDeptStaffDetailsFromDing(){
        List<DingDeptStaffDetails> dingDeptStaffDetails = new ArrayList<>();
        this.doGetDeptUserDetails(dingDeptStaffDetails, 1L, 0);
        return dingDeptStaffDetails;
    }

    /**
     * 获取根部门以外的所有子部门的员工详情
     * @param deptListEntities 所有子部门列表数据集合
     * @return 员工详情数据集合 List<DingDeptStaffDetails>
     */
    public List<DingDeptStaffDetails> doGetAllSubDeptStaffDetailsFromDing(List<DingDeptListEntity> deptListEntities){
        List<DingDeptStaffDetails> dingDeptStaffDetails = new ArrayList<>();
        if(null != deptListEntities && deptListEntities.size() != 0){
            for (DingDeptListEntity dle : deptListEntities
            ) {
                this.doGetDeptUserDetails(dingDeptStaffDetails, dle.getDept_id(), 0);
            }
        }
        return dingDeptStaffDetails;
    }

    /**
     * 获取单个部门分页员工数据详情集合
     * @param totalList 汇总前的数据集合
     * @param deptId 该部门ID
     * @param cursor 游标，从0开始，如果存在下一页，则从返回值中获取
     * @return 汇总后的该部门员工详情数据集合
     */
    public List<DingDeptStaffDetails> doGetDeptUserDetails(List<DingDeptStaffDetails> totalList, Long deptId, int cursor){
        JSONObject res;
        JSONObject param = new JSONObject();
        param.put("dept_id", deptId);
        param.put("cursor", cursor);
        param.put("size", 100);
        res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetDeptUserDetails, DingAccessUtils.token, param)).getJSONObject("result");
        String list = res.getString("list");
        if(null != list){
            totalList.addAll(JSON.parseArray(res.getString("list"), DingDeptStaffDetails.class));
        }
        boolean hasMore = Boolean.getBoolean(res.getString("has_more"));
        if(hasMore){
            int inCursor = Integer.parseInt(res.getString("next_cursor"));
            this.doGetDeptUserDetails(totalList, deptId, inCursor);
        }
        return totalList;
    }

    /**
     * 获取离职人员详情 - 钉钉规定：最大单次传入员工UserID数量为50
     * @param totalList 离职人员详情的数据集合 List<DingLeaveStaffDetailsEntity> 非数据库实体类
     * @param userIds 要查询的员工UserID集合
     * @return 离职人员详情的数据集合 List<DingLeaveStaffDetailsEntity> 非数据库实体类
     */
    public List<DingLeaveStaffDetailsEntity> doGetLeaveStaffDetailsListByUserIDs(List<DingLeaveStaffDetailsEntity> totalList, List<String> userIds){
        int j = userIds.size() / 50;
        int leftCount = userIds.size() % 50;
        for (int i = 0; i <= j; i++) {
            JSONObject param = new JSONObject();
            if(i == j){
                List<String> subLastList = userIds.subList((i * 50),(i * 50) + leftCount);
                param.put("userid_list", this.doGetUserIDsFromList(subLastList));
            }else{
                List<String> subList = userIds.subList((i * 50),((i + 1) * 50));
                param.put("userid_list", this.doGetUserIDsFromList(subList));
            }
            JSONObject res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetLeaveStaffDetailsList, DingAccessUtils.token, param));
            Boolean isSuccess = res.getBoolean("success");
            if(null != isSuccess && isSuccess){
                List<DingLeaveStaffDetailsEntity> subStaffDetailList = JSON.parseArray(res.getString("result"), DingLeaveStaffDetailsEntity.class);
                totalList.addAll(subStaffDetailList);
            }
        }
        return totalList;
    }

    /**
     * 对传入的List<String>进行转换
     * @param list 需要转换的集合
     * @return 钉钉参数需要的形式String  ("user1,user2,user3")
     */
    public String doGetUserIDsFromList(List<String> list){
        String userIDList = "";
        for (int k = 0; k < list.size() ; k++) {
            if(k == list.size() -1){
                userIDList = userIDList + list.get(k);
            }else{
                userIDList = userIDList + list.get(k) + ",";
            }
        }
        return userIDList;
    }

    /**
     * 传入一个List<ContactBasic>集合，将用户钉钉UserID抽离出后，组成List<String>
     * @param contactBasics List<ContactBasic>集合
     * @return List<String>集合
     */
    public List<String> doGetUserIds(List<ContactBasic> contactBasics){
        List<String> userIds = new ArrayList<>();
        for (ContactBasic cb : contactBasics
             ) {
            userIds.add(cb.getDingUserid());
        }
        return userIds;
    }

    /**
     * 获取指定日期内的打卡详情，最大为7天
     * @param userIds 企业内的员工ID列表，最多不能超过50个
     * @param checkDateFrom 查询考勤打卡记录的起始工作日。格式为：yyyy-MM-dd hh:mm:ss
     * @param checkDateTo 查询考勤打卡记录的结束工作日。格式为：yyyy-MM-dd hh:mm:ss  起始与结束工作日最多相隔7天
     * @return 钉钉标准返回值对象
     */
    public List<DingClockRecord> doGetDingClockRecord(List<String> userIds, String checkDateFrom, String checkDateTo){
        List<DingClockRecord> list = new ArrayList<>();
        JSONObject param = new JSONObject();
        param.put("userIds", JSON.toJSONString(userIds));
        param.put("checkDateFrom", checkDateFrom);
        param.put("checkDateTo", checkDateTo);
        JSONObject res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetAttendanceClockList, DingAccessUtils.token, param));
        String errcode = res.getString("errcode");
        if(null != errcode && errcode.equals("0")){
            JSONArray jsonArray = res.getJSONArray("recordresult");
            if(null != jsonArray){
                list = jsonArray.toJavaList(DingClockRecord.class);
            }
        }
        return list;
    }


    /**
     * 获取钉钉管理员列表
     * @return 管理员列表集合
     */
    public List<DingAdminEntity> getDingAdmins(){
        List<DingAdminEntity> admins = new ArrayList<>();
        JSONObject param = new JSONObject();
        param.put("token", DingAccessUtils.token);
        JSONObject res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetAdminList, DingAccessUtils.token, param));
        if(null != res){
            try{
                String errcode = res.getString("errcode");
                if(errcode.equals("0")){
                    JSONArray array = res.getJSONArray("result");
                    admins = array.toJavaList(DingAdminEntity.class);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return admins;
    }


    /**
     * 获取企业内的假期规则列表（假期类型）
     * @param adminid 主管理员userid
     * @return 假期规则列表集合
     */
    public List<DingVacationType> getDingVacationTypeList(String adminid){
        List<DingVacationType> typeList = new ArrayList<>();
        JSONObject param = new JSONObject();
        param.put("op_userid", adminid);
        param.put("vacation_source", "all");
        JSONObject res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetVacationTypeList, DingAccessUtils.token, param));
        if(null != res){
            try{
                String errcode = res.getString("errcode");
                if(errcode.equals("0")){
                    JSONArray array = res.getJSONArray("result");
                    for (int i = 0; i < array.size(); i++) {
                        JSONObject jsonObject = array.getJSONObject(i);
                        String leave_code = jsonObject.getString("leave_code");
                        String leave_name = jsonObject.getString("leave_name");
                        if(null != leave_code && null != leave_name){
                            DingVacationType type = new DingVacationType();
                            type.setLeaveCode(leave_code);
                            type.setLeaveName(leave_name);
                            typeList.add(type);
                        }
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return typeList;
    }

    /**
     * 获取钉钉考勤审批记录
     * @param startTime 开始时间
     * @param endTime 结束时间 最大支持查询180天内的考勤记录
     * @param userIds 用户id集合 每次最多传100个
     * @param offset 游标
     * @param totalList 接收结果的集合
     * @return totalList
     */
    public List<DingLeaveRecord> getDingLeaveRecords(long startTime, long endTime, List<String> userIds, int offset, List<DingLeaveRecord> totalList){
        String ids = this.doGetUserIDsFromList(userIds);
        JSONObject param = new JSONObject();
        param.put("userid_list", ids);
        param.put("start_time", startTime);
        param.put("end_time", endTime);
        param.put("offset", offset); //支持分页查询，与size参数同时设置时才生效，此参数代表偏移量，偏移量从0开始。
        param.put("size", 20); //支持分页查询，与offset参数同时设置时才生效，此参数代表分页大小，最大20。
        JSONObject res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetleaveStatusList, DingAccessUtils.token, param));
        if(null != res){
            Boolean isSuccess = res.getBoolean("success");
            //System.out.println("Success is " + isSuccess + " And errcode is " + res.getString("errcode"));
            if(null != isSuccess && isSuccess){
               JSONObject result = res.getJSONObject("result");
               List<DingLeaveStatus> statusList = result.getJSONArray("leave_status").toJavaList(DingLeaveStatus.class);
               //System.out.println("本次查询到的记录数：" + statusList.size());
                for (DingLeaveStatus dls : statusList
                     ) {
                    DingLeaveRecord dlr = new DingLeaveRecord();
                    dlr.setDurationPercent(dls.getDuration_percent());
                    dlr.setDurationUnit(dls.getDuration_unit());
                    dlr.setEndTime(dls.getEnd_time());
                    dlr.setStartTime(dls.getStart_time());
                    dlr.setLeaveCode(dls.getLeave_code());
                    dlr.setUserid(dls.getUserid());
                    totalList.add(dlr);
                }
                Boolean hasMore = result.getBoolean("has_more");
                //System.out.println("Has More is " + hasMore);
                if(null != hasMore && hasMore){
                    this.getDingLeaveRecords(startTime, endTime, userIds, offset + 20, totalList);
                }
            }
        }
        return totalList;
    }

    /**
     * 获取当前企业所有可管理的表单，其中包含process_code可在OA审批查询时使用
     * @return 所有可管理的表单对象列表
     */
    public List<DingProcessTemplateEntity> getAllManagedProcessTemplate(){
        List<DingProcessTemplateEntity> list = new ArrayList<>();
        List<DingAdminEntity> admins = this.getDingAdmins();
        String adminid = null;
        for (DingAdminEntity admin : admins
        ) {
            if(admin.getSys_level() == 1){ //主管理员
                adminid = admin.getUserid();
            }
        }
        JSONObject param = new JSONObject();
        param.put("userid", adminid);
        JSONObject res = JSON.parseObject(HttpUtils.doPostRequest(restTemplate, DingAPIURLs.urlForGetAllManagedProcessTemplates, DingAccessUtils.token, param));
        if(null != res){
            Boolean isSuccess = res.getBoolean("success");
            if(null != isSuccess && isSuccess){
                JSONArray result = res.getJSONArray("result");
                list = result.toJavaList(DingProcessTemplateEntity.class);
            }
        }
        return list;
    }


}
